/*
  cpgf Library
  Copyright (C) 2011, 2012 Wang Qi http://www.kbasm.com/
  All rights reserved.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/


#ifndef __GMETAINVOKE_P_H
#define __GMETAINVOKE_P_H

#include "cpgf/gpp.h"
#include "cpgf/gassert.h"
#include "cpgf/gifelse.h"
#include "cpgf/gvariant.h"
#include "cpgf/gmetacommon.h"
#include "cpgf/gmetatype.h"
#include "cpgf/gmetapolicy.h"
#include "cpgf/gmetaconverter.h"
#include "cpgf/gcallback.h"
#include "cpgf/gexception.h"


#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4267) // weird warning when reflecting std::vector in VC, disable it.
#endif


namespace cpgf {

namespace meta_internal {


#define REF_CALL_HELPER_CAST(N, unused) \
	GPP_COMMA_IF(N) fromVariant<typename FT::ArgList::Arg ## N, typename GIfElseResult<PolicyHasRule<Policy, GMetaRuleCopyConstReference<N> >, VarantCastCopyConstRef, VarantCastKeepConstRef>::Result >(*params[N])

#define REF_CALL_HELPER_CAST_EXPLICIT_THIS_HELPER(N) \
	GPP_COMMA_IF(N) fromVariant<typename FT::ArgList::Arg ## N, typename GIfElseResult<PolicyHasRule<Policy, GMetaRuleCopyConstReference<N> >, VarantCastCopyConstRef, VarantCastKeepConstRef>::Result >(*params[N - 1])

#define REF_CALL_HELPER_CAST_EXPLICIT_THIS_EMPTY(N)

#define REF_CALL_HELPER_CAST_EXPLICIT_THIS(N, unused) GPP_IF(N, REF_CALL_HELPER_CAST_EXPLICIT_THIS_HELPER, REF_CALL_HELPER_CAST_EXPLICIT_THIS_EMPTY)(N)

#define REF_CALL_HELPER(N, unused) \
	template <typename CT, typename FT, typename RT, typename Policy> \
	struct GMetaInvokeHelper<CT, FT, N, RT, Policy, false, false> { \
		static GVariant invoke(void * instance, const CT & callback, GVariant const * const * params, size_t paramCount) { \
			(void)params; \
			(void)paramCount; \
			(void)instance; \
			GVarTypeData typeData = GVarTypeData(); \
			deduceVariantType<RT>(typeData, ! PolicyHasRule<Policy, GMetaRuleParamNoncopyable<metaPolicyResultIndex> >::Result); \
			GVariant v; \
			variant_internal::InitVariant<! PolicyHasRule<Policy, GMetaRuleParamNoncopyable<metaPolicyResultIndex> >::Result, typename variant_internal::template DeducePassType<RT>::PassType>(v, typeData, callback(GPP_REPEAT(N, REF_CALL_HELPER_CAST, GPP_EMPTY))); \
			return v; \
		} \
	}; \
	template <typename CT, typename FT, typename Policy> \
	struct GMetaInvokeHelper<CT, FT, N, void, Policy, false, false> { \
		static GVariant invoke(void * instance, const CT & callback, GVariant const * const * params, size_t paramCount) { \
			(void)params; \
			(void)paramCount; \
			(void)instance; \
			callback(GPP_REPEAT(N, REF_CALL_HELPER_CAST, GPP_EMPTY)); \
			return GVariant(); \
		} \
	}; \
	template <typename CT, typename FT, typename RT, typename Policy> \
	struct GMetaInvokeHelper<CT, FT, N, RT, Policy, false, true> { \
		static GVariant invoke(void * instance, const CT & callback, GVariant const * const * params, size_t paramCount) { \
			(void)params; \
			(void)paramCount; \
			GVarTypeData typeData = GVarTypeData(); \
			deduceVariantType<RT>(typeData, ! PolicyHasRule<Policy, GMetaRuleParamNoncopyable<metaPolicyResultIndex> >::Result); \
			GVariant v; \
			variant_internal::InitVariant<! PolicyHasRule<Policy, GMetaRuleParamNoncopyable<metaPolicyResultIndex> >::Result, typename variant_internal::template DeducePassType<RT>::PassType>(v, typeData, callback((typename CT::TraitsType::ArgList::Arg0)(instance) GPP_REPEAT(N, REF_CALL_HELPER_CAST_EXPLICIT_THIS, GPP_EMPTY))); \
			return v; \
		} \
	}; \
	template <typename CT, typename FT, typename Policy> \
	struct GMetaInvokeHelper<CT, FT, N, void, Policy, false, true> { \
		static GVariant invoke(void * instance, const CT & callback, GVariant const * const * params, size_t paramCount) { \
			(void)params; \
			(void)paramCount; \
			callback((typename CT::TraitsType::ArgList::Arg0)(instance) GPP_REPEAT(N, REF_CALL_HELPER_CAST_EXPLICIT_THIS, GPP_EMPTY)); \
			return GVariant(); \
		} \
	};

template <typename CT, typename FT, unsigned int N, typename RT, typename Policy, bool IsVariadic, bool ExplicitThis>
struct GMetaInvokeHelper;

template <typename CT, typename FT, unsigned int N, typename RT, typename Policy>
struct GMetaInvokeHelper <CT, FT, N, RT, Policy, true, false>
{
	GASSERT_STATIC(N == 1);

	static GVariant invoke(void * instance, const CT & callback, GVariant const * const * params, size_t paramCount) {
		(void)instance;

		GMetaVariadicParam variadicParams;
		variadicParams.params = params;
		variadicParams.paramCount = paramCount;
		
		return createVariant<true, RT>(callback(&variadicParams), true);
	}
};

template <typename CT, typename FT, unsigned int N, typename Policy>
struct GMetaInvokeHelper <CT, FT, N, void, Policy, true, false>
{
	GASSERT_STATIC(N == 1);

	static GVariant invoke(void * instance, const CT & callback, GVariant const * const * params, size_t paramCount) {
		(void)instance;

		GMetaVariadicParam variadicParams;
		variadicParams.params = params;
		variadicParams.paramCount = paramCount;
		callback(&variadicParams);
		return GVariant();
	}
};

template <typename CT, typename FT, unsigned int N, typename RT, typename Policy>
struct GMetaInvokeHelper <CT, FT, N, RT, Policy, true, true>
{
	GASSERT_STATIC(N == 1);

	static GVariant invoke(void * instance, const CT & callback, GVariant const * const * params, size_t paramCount) {
		GVariant self(instance);
		GScopedArray<const GVariant *> tempParams(new const GVariant *[paramCount + 1]);
		
		tempParams[0] = &self;
		for(size_t i = 0; i < paramCount; ++i) {
			tempParams[i + 1] = params[i];
		}
		
		GMetaVariadicParam variadicParams;
		variadicParams.params = tempParams;
		variadicParams.paramCount = paramCount + 1;
		
		return createVariant<true, RT>(callback(&variadicParams), true);
	}
};

template <typename CT, typename FT, unsigned int N, typename Policy>
struct GMetaInvokeHelper <CT, FT, N, void, Policy, true, true>
{
	GASSERT_STATIC(N == 1);

	static GVariant invoke(void * instance, const CT & callback, GVariant const * const * params, size_t paramCount) {
		GVariant self(instance);
		GScopedArray<const GVariant *> tempParams(new const GVariant *[paramCount + 1]);
		
		tempParams[0] = &self;
		for(size_t i = 0; i < paramCount; ++i) {
			tempParams[i + 1] = params[i];
		}
		
		GMetaVariadicParam variadicParams;
		variadicParams.params = tempParams;
		variadicParams.paramCount = paramCount + 1;
		
		callback(&variadicParams);
		return GVariant();
	}
};

GPP_REPEAT_2(REF_MAX_ARITY, REF_CALL_HELPER, GPP_EMPTY)

template <typename FunctionTraits>
struct CheckSingleArity
{
	G_STATIC_CONSTANT(bool, Result = (FunctionTraits::Arity == 1));
};

template <typename FunctionTraits, typename Enabled = void>
struct IsVariadicFunction
{
	typedef FunctionTraits TraitsType;

	G_STATIC_CONSTANT(bool, Result = false);
};

template <typename FunctionTraits>
struct IsVariadicFunction <FunctionTraits, typename GEnableIfResult<CheckSingleArity<FunctionTraits> >::Result>
{
	typedef FunctionTraits TraitsType;

	G_STATIC_CONSTANT(bool, Result = (IsSameType<typename TraitsType::ArgList::Arg0, GMetaVariadicParam *>::Result
		|| IsSameType<typename TraitsType::ArgList::Arg0, const GMetaVariadicParam *>::Result)
	);
};



} // namespace meta_internal


} // namespace cpgf


#undef REF_CALL_HELPER
#undef REF_CALL_HELPER_CAST
#undef REF_CALL_HELPER_CAST_EXPLICIT_THIS
#undef REF_CALL_HELPER_CAST_EXPLICIT_THIS_HELPER
#undef REF_CALL_HELPER_CAST_EXPLICIT_THIS_EMPTY


#if defined(_MSC_VER)
#pragma warning(pop)
#endif


#endif
